/*========================================================================
   Copyright (c) Ars Electronica Futurelab, 2008
  ========================================================================*/

#ifndef  ___TCPMANAGER__H__
#define  ___TCPMANAGER__H__

#pragma once

/***************************************************************
	USAGE
****************************************************************

TCP Socket Client:
------------------

1) create()
2) connect()
3) send()
...
x) close()

optional:
SetTimeoutSend()


TCP Socket Server:
------------------

1) create()
2) bind()
3) listen()
4) accept()
5) receive()
...
x) close()

optional:
SetTimeoutAccept()
SetTimeoutReceive()

****************************************************************/

#ifdef WIN32
	#include <winsock2.h>
#else
	#include <sys/types.h>
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <arpa/inet.h>
	#define USHORT unsigned short
	#define ULONG unsigned long
	#define SOCKET int
	#define INVALID_SOCKET (-1)
	#define SOCKET_ERROR (-1)
#endif
#include <string.h>
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>

/// Socket constants.
#define SOCKET_TIMEOUT      (SOCKET_ERROR - 1)
#define NO_TIMEOUT          (-1)
#define DEFAULT_TIMEOUT     NO_TIMEOUT


//--------------------------------------------------------------------------------
class InetAddr : public sockaddr_in
{
public:
  // constructors
	InetAddr()
	{
		memset(this, 0, sizeof(InetAddr));
		sin_family= AF_INET;
		sin_port= 0;
		sin_addr.s_addr= 0;
	};

	InetAddr(const struct sockaddr& sockAddr)
	{
		memcpy(this, &sockAddr, sizeof(struct sockaddr));
	};

	InetAddr(const struct sockaddr_in& sin)
	{
		memcpy(this, &sin, sizeof(struct sockaddr_in));
	};

	InetAddr(const ULONG ulAddr, const USHORT ushPort= 0)
	{
		memset(this, 0, sizeof(InetAddr));
		sin_family= AF_INET;
		sin_port= htons(ushPort);
		sin_addr.s_addr= htonl(ulAddr);
	};

	InetAddr(const wchar_t* pStrIP, const USHORT usPort= 0)
	{
		char szStrIP[32];

#ifdef WIN32
		WideCharToMultiByte(CP_ACP, 0, pStrIP, (int)wcslen(pStrIP) + 1, szStrIP, 32, 0, 0);
#else
        wcstombs(szStrIP, pStrIP, sizeof(szStrIP));
#endif
		memset(this, 0, sizeof(InetAddr));
		sin_family= AF_INET;
		sin_port= htons(usPort);
		sin_addr.s_addr= inet_addr(szStrIP);
	}

	InetAddr(const char* pStrIP, const USHORT usPort= 0)
	{
		memset(this, 0, sizeof(InetAddr));
		sin_family= AF_INET;
		sin_port= htons(usPort);
		sin_addr.s_addr= inet_addr(pStrIP);
	}

	/// returns the address in dotted-decimal format
	const char* DottedDecimal() const
	{
		return inet_ntoa(sin_addr);
	}
	USHORT GetPort() const
	{
		return ntohs(sin_port);
	}
	ULONG GetIpAddr() const
	{
		return ntohl(sin_addr.s_addr);
	}

	/// operators added for efficiency
	const InetAddr& operator=(const struct sockaddr& sa)
	{
		memcpy(this, &sa, sizeof(struct sockaddr));
		return *this;
	}
	const InetAddr& operator=(const struct sockaddr_in& sin)
	{
		memcpy(this, &sin, sizeof(struct sockaddr_in));
		return *this;
	}
	operator struct sockaddr()
	{
		return *((struct sockaddr *)this);
	}
	operator struct sockaddr *()
	{
		return (struct sockaddr *)this;
	}
	operator struct sockaddr_in *()
	{
		return (struct sockaddr_in *) this;
	}
};

typedef const InetAddr*		LPCINETADDR;
typedef InetAddr*			LPINETADDR;


//--------------------------------------------------------------------------------
/// Implementation of a TCP socket.
class TCPManager
{
public:
	TCPManager();
	virtual ~TCPManager();

	bool Close();
	bool Create();
	bool Connect(const char *pHost, USHORT usPort);
	bool Bind(USHORT usPort);
	bool Listen(int iMaxConnections = SOMAXCONN);
	bool Accept(TCPManager& sock);

	int Send(const void* pBuff, const int iSize);		//sends the data, but it is not guaranteed that really all data will be sent
	int SendAll(const void* pBuff, const int iSize);	//all data will be sent guaranteed.
	int Receive(void* pBuff, const int iSize);
	int ReceiveAll(void* pBuff, const int iSize);
	int Write(const void* pBuff, const int iSize);

	bool GetRemoteAddr(LPINETADDR pIntAddr);
	bool GetInetAddr(LPINETADDR pInetAddr);

	bool SetReceiveBufferSize(int sizeInByte);
	bool SetSendBufferSize(int sizeInByte);
	int GetReceiveBufferSize();
	int GetSendBufferSize();

	bool CheckHost(const char *pHost);
	void CleanUp();

	void SetTimeoutConnect(long timeoutInSeconds)
	{
		m_lTimeoutConnect= timeoutInSeconds;
	}
	void SetTimeoutAccept(long timeoutInSeconds)
	{
		m_lTimeoutAccept= timeoutInSeconds;
	}
	void SetTimeoutSend(long timeoutInSeconds)
	{
		m_lTimeoutSend= timeoutInSeconds;
	}
	void SetTimeoutReceive(long timeoutInSeconds)
	{
		m_lTimeoutReceive= timeoutInSeconds;
	}
	long GetTimeoutConnect()
	{
		return m_lTimeoutConnect;
	}
	long GetTimeoutAccept()
	{
		return m_lTimeoutAccept;
	}
	long GetTimeoutSend()
	{
		return m_lTimeoutSend;
	}
	long GetTimeoutReceive()
	{
		return m_lTimeoutReceive;
	}
	int GetMaxConnections()
	{
		return m_iMaxConnections;
	}

protected:
	bool SetBlocking(bool bIsBlockingA);

protected:
	int m_iListenPort;
	int m_iMaxConnections;
	SOCKET m_hSocket;

	long m_lTimeoutConnect;
	long m_lTimeoutAccept;
	long m_lTimeoutSend;
	long m_lTimeoutReceive;

	static bool m_bWinsockInit;
};

#endif // ___TCPMANAGER__H__
